package com.meizhao.api.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.meizhao.api.configuration.ConfigProperties;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;

import static com.meizhao.api.utils.constants.Keys.REDIS_KEY_DELIMITER;
import static com.meizhao.api.utils.constants.Keys.TOKEN;

public final class HttpUtils {

    private static final ConfigProperties configProperties = (ConfigProperties) SpringContextUtils.getBean("configProperties");

    public static boolean isAjax(HttpServletRequest request){
        String ajaxHeader = "x-requested-with";
        String ajax = "XMLHttpRequest";
        return !StringUtils.isBlank(request.getHeader(ajaxHeader)) && request.getHeader(ajaxHeader).equalsIgnoreCase(ajax);
    }

    /**
     * 存在bug,稍后修改
     */
    public static String getIpAddress() {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if(request == null){
            return null;
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    public static String getURI(HttpServletRequest request) throws IllegalStateException {
        String uri = request.getRequestURI();
        String contextPath = request.getContextPath();
        int start = uri.indexOf(contextPath);
        return uri.substring(start + contextPath.length());
    }

    public static Boolean checkUrl(String currentUrl, List<String> passUrls){

        boolean checkAuth = true;
        if (PatternMatchUtils.simpleMatch(passUrls.toArray(new String[passUrls.size()]), currentUrl)) {
            checkAuth = false;
        }
        return checkAuth;
    }

    public static Boolean checkAuthUrl(String currentUrl, List<String> apiUrls){
        for(String url : apiUrls){
            if(currentUrl.startsWith(url)){
                return true;
            }
        }
        return false;
    }

    public static String getTokenByRequest() {

        HttpServletRequest request = getRequest();
        if(request == null){
            return null;
        }
        String token = CookieUtils.getValue(request, TOKEN);
        if(token != null){
            return token;
        }
        String contentType = request.getContentType();
        if(StringUtils.isBlank(contentType)){
            return request.getParameter(TOKEN);
        }
        if(contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)){
            String jsonStr = (String)getRequestData(request);
            if(StringUtils.isNotBlank(jsonStr)){
                try {
                    Map dto = getObjectMapper().readValue(jsonStr, Map.class);
                    if(dto != null) {
                        return String.valueOf(dto.get(TOKEN));
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    return null;
                }
            }
        }else if(contentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)){
            Map<String, String[]> map = request.getParameterMap();
            if(map != null && map.size() > 0){
                return map.get("token") != null ? map.get(TOKEN)[0] : null;
            }
        }else if(contentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)){
            return request.getParameter(TOKEN);
        }
        return null;
    }

    private static Object getRequestData(HttpServletRequest request){

        try {
            return IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        }catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }

    public static ObjectMapper getObjectMapper(){

        ObjectMapper objectMapper = new Jackson2ObjectMapperBuilder().findModulesViaServiceLoader(true)
                .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(
                        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(
                        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .build();
        SimpleModule simpleModule = new SimpleModule();

        /**
         *  将Long,BigInteger序列化的时候,转化为String
         */
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);

        objectMapper.registerModule(simpleModule);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                    throws IOException {
                jsonGenerator.writeString("");
            }
        });
        return objectMapper;
    }

    public static HttpServletRequest getRequest(){

        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static String getContextUrl(HttpServletRequest request){

        return request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";
    }

    public static String getContextUrl(){

        return getContextUrl(getRequest());
    }

    public static String createToken(String prefix, String suffix){

        return SymmetricEncoder.AESEncode(configProperties.getLoginEncodeRules(), prefix + REDIS_KEY_DELIMITER + suffix);
    }

    public static String[] decodeToken(String token){

        if(StringUtils.isBlank(token) || "null".equals(token)){
            return null;
        }
        token = SymmetricEncoder.AESDecode(configProperties.getLoginEncodeRules(), token);
        return StringUtils.isNotBlank(token) ? token.split(REDIS_KEY_DELIMITER) : null;
    }

    public static String[] decodeToken(){

        String token = getTokenByRequest();
        if(StringUtils.isBlank(token)){
            return null;
        }
        return decodeToken(token);
    }
}
